官方 Demo:https://vueuse.org/core/useElementVisibility/
useElementVisibility 算是昨天 useIntersectionObserver 的應用,可以搭配著看。
// src/compositions/useElementVisibility.js
import { ref } from 'vue'
import { defaultWindow } from '@/helper'
import { useIntersectionObserver } from '@/compositions/useIntersectionObserver'
export function useElementVisibility(element, options = {}) {
const { window = defaultWindow, scrollTarget, threshold = 0 } = options
const elementIsVisible = ref(false)
useIntersectionObserver(
element,
([{ isIntersecting }]) => {
elementIsVisible.value = isIntersecting
},
{
root: scrollTarget,
window,
threshold,
},
)
return elementIsVisible
}
簡易版的部分是指傳入 useIntersectionObserver 的第二個參數 callback function,晚點會貼原始碼的版本。
先大概看一下這段在做的事,當 element 進到瀏覽器 viewport 時(root 為 undefined,預設會找 document viewport),回執行我們傳入的 callback function:
([{ isIntersecting }]) => {
elementIsVisible.value = isIntersecting
}
最後 return elementIsVisible 出去給上層使用,看官方 Demo 那個右下角的 "inside", "outside",就是拿 elementIsVisible 狀態做判斷的。
([{ isIntersecting }]) => {
elementIsVisible.value = isIntersecting
}
根據昨天看 useIntersectionObserver 的 Demo code 原始碼,用法也是上面這種簡易版本,關於 useElementVisibility 的 Demo,我用上面簡易版本的 callback function 實作起來沒遇到什麼問題,所以今天在看到原始碼的時候,覺得為什麼變複雜了 XD。
先來看一下目前版本可能會遇到什麼問題,可以參考官方 PR:https://github.com/vueuse/vueuse/pull/3365
PR 描述大概是在說,我們目前透過解構,固定拿第一個 entry,不一定是最新的。
接著看 mdn 文件關於 entry 排序的說明:
The list of entries received by the callback includes one entry for each threshold-crossing event — multiple entries can be received at a time, either from multiple targets or from a single target crossing multiple thresholds in a short amount of time. The entries are dispatched using a queue, so they should be ordered by the time they were generated, but you should preferably use IntersectionObserverEntry.time to correctly order them.
看起來是拿到最新的 entry 需要透過 entry.time 來判斷,知道問題在哪後,來看看原始碼中 callback function 是怎麼實現的:
(intersectionObserverEntries) => {
let isIntersecting = elementIsVisible.value
// Get the latest value of isIntersecting based on the entry time
let latestTime = 0
for (const entry of intersectionObserverEntries) {
if (entry.time >= latestTime) {
latestTime = entry.time
isIntersecting = entry.isIntersecting
}
}
elementIsVisible.value = isIntersecting
},
每次執行 callback 的時候,會把每個 entry 拿出來檢查 entry.time 是否是目前最大的,是的話代表這次是目前最新的狀態,直到跑完整個 intersectionObserverEntries,就可以得到這次 callback 中最新的 entry,最後更新 elementIsVisible.value。
GitHub PR:https://github.com/RhinoLee/30days_vue/pull/23/files
今天到這邊告一段落~ 明天會繼續看 useElementVisibility 的單元測試是怎麼測的。